package com.docker.comment;

import com.alibaba.druid.util.StringUtils;
import com.docker.core.mvc.Remark;
import com.docker.core.third.websocket.WebsocketKit;
import com.docker.manege.server.DockerServerDao;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.model.Frame;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jfinal.aop.Aop;
import com.jfinal.kit.Okv;
import com.jfinal.plugin.activerecord.Record;
import kit.docker.exception.BaseException;

import java.io.*;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * @Author zhl
 * @Create 2021/1/18 14:26
 **/
@Remark("")
public class ShellUtil {
    //存放ssh连接信息的map
    private static Map<String, SSHConnectInfo> sshMap = new ConcurrentHashMap<>();
    private static Map<String, DockerConnectInfo> dockerMap = new ConcurrentHashMap<>();
    //线程池
    private static ExecutorService executorService = Executors.newCachedThreadPool();

    private static DockerServerDao dockerServerDao = Aop.get(DockerServerDao.class);

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/21 15:27
     **/
    @Remark("获取服务连接")
    private static final SSHConnectInfo getSSHConnectInfo(String connectKey, javax.websocket.Session webSocketSession) {
        if (sshMap.containsKey(connectKey)) {
            return sshMap.get(connectKey);
        } else {
            JSch jSch = new JSch();
            SSHConnectInfo sshConnectInfo = new SSHConnectInfo();
            sshConnectInfo.setjSch(jSch);
            sshConnectInfo.setWebSocketSession(webSocketSession);
            sshMap.put(connectKey, sshConnectInfo);
            return sshConnectInfo;
        }
    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/21 15:28
     **/
    @Remark("连接服务器")
    private static void connectToSSH(String cmd, javax.websocket.Session webSocketSession, String identityPath, String host, String username, int j, String connectKey) throws JSchException, IOException {
        SSHConnectInfo sshConnectInfo = getSSHConnectInfo(connectKey, webSocketSession);
        Session session = null;
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");
        //获取jsch的会话
        sshConnectInfo.getjSch().addIdentity(identityPath);
        session = sshConnectInfo.getjSch().getSession(username, host);
        session.setConfig(config);
        //连接  超时时间30s
        session.connect(30000);

        //开启shell通道
        Channel channel = session.openChannel("shell");

        //通道连接 超时时间3s
        channel.connect(3000);

        //设置channel
        sshConnectInfo.setChannel(channel);
        transToSSH(channel, cmd);

        //读取终端返回的信息流
        InputStream inputStream = channel.getInputStream();
        try {
            //循环读取
            byte[] buffer = new byte[1024];
            int i = 0;
            //如果没有数据来，线程会一直阻塞在这个地方等待数据。
            while ((i = inputStream.read(buffer)) != -1) {
                if (j > 0) {
                    j--;
                    continue;
                }
                WebsocketKit.sendMsg(webSocketSession, null, new Record().set("data", new String(Arrays.copyOfRange(buffer, 0, i))));
            }

        } finally {
            System.err.println("连接关闭了");
            //断开连接后关闭会话
            channel.disconnect();
            session.disconnect();
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/21 15:28
     **/
    @Remark("根据sessionID发送消息")
    public static void transToSSH(String connectKey, String command) {
        Channel channel = sshMap.get(connectKey).getChannel();
        transToSSH(channel, command);
    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/21 15:29
     **/
    @Remark("根据通道发送消息")
    public static void transToSSH(Channel channel, String command) {
        try {
            if (channel != null) {
                OutputStream outputStream = channel.getOutputStream();
                outputStream.write(command.getBytes());
                outputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/28 13:38
     **/
    @Remark("连接docker容器并执行命令")
    public static void transToDocker(String connectKey, String command) {
        if (dockerMap.containsKey(connectKey)) {
            PipedOutputStream outputStream = dockerMap.get(connectKey).getOutputStream();
            try {
                outputStream.write(command.getBytes());
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            throw new BaseException("");
        }

    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/28 14:11
     **/
    @Remark("获取输出流")
    public static DockerConnectInfo getDockerConnectInfo(String connectKey) {
        if (dockerMap.containsKey(connectKey)) {
            return dockerMap.get(connectKey);
        } else {
            return null;
        }
    }

    public static void putOutputStream(String connectKey) {
        PipedInputStream inputStream = new PipedInputStream();
        dockerMap.put(connectKey, new DockerConnectInfo(connectKey, inputStream));
    }

    /**
     * @Param: [containerName 容器名字 session  serverId 服务器id j 跳过前几次返回数据]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/21 15:30
     **/
    @Remark("线程池 创建连接")
    public static void connect(String cmd, javax.websocket.Session session, String serverId, int j, String connectKey) {
        Record info = dockerServerDao.info(serverId);
        String OS = System.getProperty("os.name").toLowerCase();
        if (OS.contains("windows")) {
            info.set("cert", info.getStr("cert_window"));
        }
        if ((StringUtils.isEmpty(info.getStr("cert")) && StringUtils.isEmpty(info.getStr("cert_window")))
                || StringUtils.isEmpty(info.getStr("docker_host"))
                || StringUtils.isEmpty(info.getStr("docker_host"))) {
            WebsocketKit.sendMsg(session, null, Okv.by("data", "服务器信息不存在,连接正在关闭"));
            WebsocketKit.closeSocket(session);
            return;
        }
//        创建 服务器连接
        executorService.execute(() -> {
                    try {
                        ShellUtil.connectToSSH(cmd, session, info.getStr("cert"), info.getStr("docker_host"), info.get("username", "root"), j, connectKey);
                    } catch (JSchException | IOException e) {
                        e.printStackTrace();
                    }
                }
        );
    }


    public static class SSHConnectInfo {
        private javax.websocket.Session webSocketSession;
        private JSch jSch;
        private Channel channel;

        public javax.websocket.Session getWebSocketSession() {
            return webSocketSession;
        }

        public void setWebSocketSession(javax.websocket.Session webSocketSession) {
            this.webSocketSession = webSocketSession;
        }

        public JSch getjSch() {
            return jSch;
        }

        public void setjSch(JSch jSch) {
            this.jSch = jSch;
        }

        public Channel getChannel() {
            return channel;
        }

        public void setChannel(Channel channel) {
            this.channel = channel;
        }

        public void close(String connectKey) {
            //断开连接
            if (channel != null) {
                channel.disconnect();
            }
            //map中移除
            sshMap.remove(connectKey);
        }
    }

    //    docker连接实例
    public static class DockerConnectInfo {
        private PipedOutputStream outputStream;
        private PipedInputStream inputStream;
        private ResultCallback resultCallback;

        public PipedOutputStream getOutputStream() {
            return outputStream;
        }

        public ResultCallback getResultCallback() {
            return resultCallback;
        }

        public PipedInputStream getInputStream() {
            return inputStream;
        }

        public DockerConnectInfo(String connectKey, PipedInputStream inputStream) {
            outputStream = new PipedOutputStream();
            this.inputStream = inputStream;
            try {
                outputStream.connect(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
            resultCallback = new ResultCallback<Frame>() {
                @Override
                public void onStart(Closeable closeable) {
                    WebsocketKit.sendMsg(connectKey, null, new Record().set("data", "容器连接成功\r"));
                }

                @Override
                public void onNext(Frame object) {
                    WebsocketKit.sendMsg(connectKey, null, new Record().set("data", new String(object.getPayload())));
                }

                @Override
                public void onError(Throwable throwable) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    throwable.printStackTrace();
                    WebsocketKit.sendMsg(connectKey, null, new Record().set("data", throwable.getMessage()));
                }

                @Override
                public void onComplete() {
                    // TODO 需要关闭前端socket
                    WebsocketKit.sendMsg(connectKey, null, new Record().set("data", "容器连接正在关闭\n"));
                    WebsocketKit.closeSocket(connectKey);
                }

                @Override
                public void close() {
                    WebsocketKit.sendMsg(connectKey, null, new Record().set("data", "容器连接已关闭\n"));
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
        }

        public void close() {
            try {
                outputStream.write("exit\r".getBytes());
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                System.err.println("连接超时关闭");
//                e.printStackTrace();
            }
        }
    }

    /**
     * @Param: [null]
     * @return: TODO
     * @Author: zhl
     * @Create: 2021/1/22 10:36
     **/
    @Remark("关闭服务器连接")
    public static void close(String connectKey) {
        SSHConnectInfo sshConnectInfo = sshMap.get(connectKey);
        DockerConnectInfo dockerConnectInfo = dockerMap.get(connectKey);
        if (dockerConnectInfo!=null){
            dockerConnectInfo.close();
        }
        if (sshConnectInfo != null) {
            sshConnectInfo.close(connectKey);
        }
    }

}
